package Derivation;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import TopK.EquationTopK;
import Basics.*;

public class DeriveByRuleBottomUp extends DeriveByRule
{
	Map<Atom, Vector<Atom>> previouslyFoundRelevants = new HashMap<Atom, Vector<Atom>>();
	
	Map<Atom, HashSet<Body>> provenance;
	
	Set<Atom> addedInstAtomsInThisIteration;
	
	EquationTopK topk;
	
	HashSet<Rule> triedBefore = new HashSet<Rule>();
	
	Map<String, Atom> keyMap;
	
	Set<Atom> atomsAddedSoFar;
	
	
	
	public DeriveByRuleBottomUp (Rule ir, Program ip, Map<Atom, HashSet<Body>> provenanceProgram, Map<String, Atom> ikeyMap, Set<Atom> addedInstAtomsInLastIteration, EquationTopK itopk)
	{
		super(ir, ip);
		this.provenance = provenanceProgram;
		this.keyMap = ikeyMap;
		this.addedInstAtomsInThisIteration = addedInstAtomsInLastIteration;
		this.topk = itopk;
	}


	
	public Set<Atom> getAtomsAddedSoFar() 
	{
		return atomsAddedSoFar;
	}



	public void setAtomsAddedSoFar(Set<Atom> atomsAddedSoFar) 
	{
		this.atomsAddedSoFar = atomsAddedSoFar;
	}

	
	


	/*public Set<Atom> getTreesUpdatedLastIter() 
	{
		return treesUpdatedLastIter;
	}



	public void setTreesUpdatedLastIter(Set<Atom> treesUpdatedLastIter) 
	{
		this.treesUpdatedLastIter = treesUpdatedLastIter;
	}*/



	/*************************************************************************************************************/
	/** Title: FindDerivationsForRuleBottomUp																				
	/** Description: Finds all Derivations for the rule according to db 				
	/*************************************************************************************************************/
	
	public void FindDerivationsForRuleBottomUp (List<Rule> instRuleVector, boolean forIntersection, int level) 
	{
		if (true == instRuleVector.isEmpty()) 
		{
			instRuleVector.add(new Rule(this.r));
		}
		
		while (false == instRuleVector.isEmpty())
		{
			instRuleVector = InstIterationBottomUp(instRuleVector, forIntersection, level);
		}
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: InstIterationBottomUp																				
	/** Description: One iteration to update the instRuleVector in partly inst rules  				
	/*************************************************************************************************************/

	public List<Rule> InstIterationBottomUp (List<Rule> instRuleVector, boolean forIntersection, int level)
	{
		/*long startTime = System.currentTimeMillis();*/
		Vector<Atom> relevants;
		for (Rule rule : instRuleVector)
		{
			for (Atom partlyInstAtom : rule.getBody().getAtoms()) 
			{
				if (true == forIntersection || false == partlyInstAtom.isFullyInst()) 
				{
					relevants = FindRelevantInstAtoms(partlyInstAtom);
					for (Atom atom : relevants) 
					{
						Rule temp = new Rule (rule);
						temp.SwapToInstAtomAndPartiallyInst(partlyInstAtom, atom);							
						if (true == temp.getIsFullyInst())
						{
							temp.getBody().setRuleUsed(this.r);//added for the top-k
							AddToProv(temp, level);
						}

						//temp isn't fully inst
						else
						{ 
							instRuleVector.add(temp);
						}
					}
				}
			}
			
			instRuleVector.remove(rule);
		}
		
		/*long endTime = System.currentTimeMillis();
		if ((endTime-startTime) > 1000) {
			System.out.println("iteration time: "  + (endTime-startTime));
			System.out.println("size of instRuleVector after iteration: " + instRuleVector.size());
		}*/
		
		return instRuleVector;
	}
	
	
	
	
	/*************************************************************************************************************/
	/** Title: AddToProv																				
	/** Description: 				
	/*************************************************************************************************************/
	
	public void AddToProv (Rule rule, int level)
	{
		if (false == this.keyMap.containsKey(rule.getHead().toString()))
		{
			this.provenance.put(rule.getHead(), new HashSet<Body>());
			keyMap.put(rule.getHead().toString(), rule.getHead());
		}
		
		if (false == rule.getHead().TreesContainBody(rule.getBody()))
		{
			Body pointerBody = new Body();
			pointerBody.setDerivedInlevel(level);
			for (Atom atom : rule.getBody().getAtoms()) 
			{
				if (true == atom.isFact() && false == this.keyMap.containsKey(atom.toString()))
				{
					this.provenance.put(atom, new HashSet<Body>());
					this.keyMap.put(atom.toString(), atom);
					this.topk.SetTreeForFact(atom);
				}
				
				pointerBody.getAtoms().add(this.keyMap.get(atom.toString()));
			}
			
			pointerBody.setRuleUsed(rule.getBody().getRuleUsed());
			
			this.provenance.get(rule.getHead()).add(pointerBody);
			
			this.addedInstAtomsInThisIteration.add(this.keyMap.get(rule.getHead().toString()));
			
			this.topk.UpdateTreesWhileSemiNaive(this.keyMap.get(rule.getHead().toString()), pointerBody);
		}
	}
	
	
	/*************************************************************************************************************/
	/** Title: FindRelevantInstAtoms																				
	/** Description: Finds the relevant atoms for the current partial inst. in the rule 				
	/*************************************************************************************************************/
	
	public Vector<Atom> FindRelevantInstAtoms (Atom partlyInstAtom)
	{
		Vector<Atom> relevantAtoms = new Vector<Atom>();
		boolean neverAppeared = true;
		
		if (false == this.previouslyFoundRelevants.isEmpty())
		{
			for (Atom key : this.previouslyFoundRelevants.keySet()) 
			{
				if (true == key.HasSameRelevantFacts(partlyInstAtom)) 
				{
					relevantAtoms = previouslyFoundRelevants.get(key);
					neverAppeared = false;
					break;
				}
			}
		}
		if (true == neverAppeared)
		{
			if (false == partlyInstAtom.isFullyInst()) 
			{
				//relevantAtoms = DB.getInstance().GetRelevantFactsFromDB(partlyInstAtom, this.p);
				relevantAtoms = MemDB.getInstance().GetRelevantFactsFromDB(partlyInstAtom, this.p);
				previouslyFoundRelevants.put(partlyInstAtom, relevantAtoms);
			}
			
			else
			{
				//if (DB.getInstance().ContainedInTable(partlyInstAtom))
				if (MemDB.getInstance().ContainedInTable(partlyInstAtom))
				{
					relevantAtoms.add(partlyInstAtom);
				}
			}
			
		}
		
		return relevantAtoms;
	}
}
